#ifndef SANDBOX_SDK_UTIL_ALLOCATOR_DETAILS_HPP
#define SANDBOX_SDK_UTIL_ALLOCATOR_DETAILS_HPP

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <features.h>
#include <malloc.h>
#include <assert.h>

#if _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600
//some functions may be moved to stdlib.h
#include <stdlib.h>
#endif

#include <memory>
#include <stdexcept>
#include <type_traits>

#include "../details/constexpr.hpp"

namespace sandbox
{
	namespace sdk 
	{
		namespace detail
		{
			//OS-specific aligned allocators.
			//return memory chunks of size 'size' aligned at 'size'
			//and free them
#if   defined(_WIN32)
			inline void *alloc(size_t size, size_t align) throw()
			{
				return ::_aligned_malloc(size, align);
			}
			inline void  free(void * mem) throw()
			{
				::_aligned_free(mem);
			}
#elif (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) || (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 600)
			inline void *alloc(size_t size, size_t align) throw()
			{
				void * result;
				if(0==::posix_memalign(result,size, align))return result;
				else return NULL;
			}	
			inline void  free(void * mem) throw()
			{
				::free(mem);
			}
#elif defined(_POSIX_C_SOURCE)
			inline void *alloc(size_t size,size_t align) throw()
			{
				return ::memalign(result,size,align);
			}	
			inline void  free(void * mem) throw()
			{
				::free(mem);
			}
#else
#warning "external implementation of aligned allocator is required"
			void *alloc(size_t size,size_t align) throw()
			{
				return ::malloc(detail::sizealign(size,4096));
			}
			void  free(void * mem) throw ()
			{
				::free(mem);
			}
#endif
			//CALL FORWARDING
			template<class T>
			typename std::enable_if<std::is_pointer<T>::value, void*>::type 
				forward_alloc(T t, size_t n) 
			{
				return t->allocate(n);
			}
			template<class T>
			typename std::enable_if<std::is_reference<T>::value, void*>::type 
				forward_alloc(T t, size_t n) 
			{
				return t.allocate(n);
			}
			template<class T>
			typename std::enable_if<std::is_class<T>::value, void*>::type 
				forward_alloc(T& t, size_t n) 
			{
				return t.allocate(n);
			}
			 
			template<class T>
			typename std::enable_if<std::is_pointer<T>::value, void>::type 
				forward_dealloc(T t, void * p, size_t n) 
			{
				return t->allocate(p, n);
			}
			template<class T>
			typename std::enable_if<std::is_reference<T>::value, void>::type 
				forward_dealloc(T t, void * p, size_t n) 
			{
				return t.deallocate(p, n);
			}
			template<class T>
			typename std::enable_if<std::is_class<T>::value, void>::type 
				forward_dealloc(T& t, void * p, size_t n) 
			{
				return t.deallocate(p, n);
			}
			
			template<class T>
			typename std::enable_if<std::is_pointer<T>::value, size_t>::type 
				forward_maxsize(const T t) 
			{
				return t->max_size();
			}
			template<class T>
			typename std::enable_if<std::is_reference<T>::value, size_t>::type 
				forward_maxsize(const T t) 
			{
				return t.max_size();
			}
			template<class T>
			typename std::enable_if<std::is_class<T>::value, size_t>::type 
				forward_maxsize(const T& t) 
			{
				return t.max_size();
			}
		}
		//==============================================================
		//wrappers for os aligned allocators what can throw
		namespace aligned
		{
			void * alloc(size_t size, size_t align=4096) throw(std::bad_alloc)
			{
				assert(::sandbox::sdk::detail::is_power_of_2(size));
				void *result=::sandbox::sdk::detail::alloc(size,align);
				if(!result)throw std::bad_alloc();
				return result;
			}
			void   free(void* mem) throw()
			{
				::sandbox::sdk::detail::free(mem);
			}
		}
		
		template <class T, 
				  template <class X, class ... Other> class InternalAllocator, 
				  class ... Args>
		class AllocatorTemplate	
		{	
			typedef InternalAllocator<T,Args ...> Allocator_t;
			Allocator_t  m_allocator_impl;
			
			static_assert(std::is_enum<decltype(Allocator_t::allocation_unit)>::value,
				"Allocator implementation does not conform to AllocatorTemplate requirements(allocation_unit must be enum)");
				
			static_assert(std::is_member_function_pointer<decltype(&Allocator_t::allocate)>::value,
                "Allocator implementation does not contain 'allocate' method");
			static_assert(std::is_member_function_pointer<decltype(&Allocator_t::deallocate)>::value,
                "Allocator implementation does not contain 'deallocate' method");
			static_assert(std::is_member_function_pointer<decltype(&Allocator_t::max_size)>::value,
                "Allocator implementation does not contain 'max_size' method");

			static_assert(Allocator_t::allocation_unit >= sizeof(T), 
				"Allocation size of allocator implementation if insufficient");


		public:
			typedef 		T  value_type;
			typedef         T* pointer;
			typedef  const  T* const_pointer; 
			typedef    size_t  size_type;
			typedef         T& reference;
			typedef  const  T& const_reference;
			template< class U > struct rebind { typedef AllocatorTemplate<U,InternalAllocator,Args ... > other; };
			
			AllocatorTemplate(){}
			//AllocatorTemplate(Allocator_t& i):m_allocator_impl(i){}
			~AllocatorTemplate()throw(){}
							  
			template <class ... Args>
			AllocatorTemplate(Args ... args):m_allocator_impl(args){}
			
			T* allocate(size_type n1, std::allocator<void>::const_pointer hint = 0)
			{
				return (T*)::sandbox::sdk::detail::forward_alloc<Allocator_t>(m_allocator_impl,n1);
			}
			void deallocate(T* p,size_type n1) throw() 
			{
				::sandbox::sdk::detail::forward_dealloc<Allocator_t>(m_allocator_impl,p,n1);
			}
			size_t max_size()const
			{
				return ::sandbox::sdk::detail::forward_maxsize<Allocator_t>(m_allocator_impl);
			}			
			
			template< class U, class... Args1 >
			void construct( U* p, Args1&&... args )
			{
				::new((void *)p) U(std::forward<Args1>(args)...);
			}
			template< class U >
			void destroy( U* p )
			{
				p->~U();
			}
			
			bool operator ==(const AllocatorTemplate& other)const
			{return m_allocator_impl==other.m_allocator_impl;}
			bool operator !=(const AllocatorTemplate& other)const
			{return m_allocator_impl!=other.m_allocator_impl;}
		private:
			//AllocatorTemplate(const AllocatorTemplate& other)=delete;
			//AllocatorTemplate& operator =(const AllocatorTemplate& other)=delete;
			//AllocatorTemplate(const AllocatorTemplate&& other)=delete;
			//AllocatorTemplate& operator =(const AllocatorTemplate&& other)=delete;
		};
	}
};

#endif //SANDBOX_SDK_UTIL_ALLOCATOR_DETAILS_HPP

